Attention Mechanisms¶
在心理学上,动物需要在复杂环境下有效关注值得注意的点。
- 人类根据 非自主性提示 和 自主性提示 选择注意点
- 非自主性提示 是基于环境中物体的突出性和易见性,引起人的注意
- 自主性提示 则是受主观意愿推动去注意
卷积、全连接、池化层都只考虑“非自主性提示”,哪个显眼就注意哪个。
而注意力机制则考虑 “自主性提示”:
- 该提示被称为 查询query
- 每个输入是一个 值value 和非自主性提示 key 的对,即key-value pair
- 通过注意力池化层来有偏向性的选择某些输入
1. 注意力汇聚¶
Average pooling 肯定是不行的,没有将key和value联系起来。
3.1 Nonparametric attention pooling¶
用Nadaraya-Watson核回归实现非参数的注意力汇聚:
- 给定数据
,根据输入的位置对输出 进行加权。 是一个衡量距离的核函数。
受此启发,得到通用 attention pooling 公式: $$ f(x) = \sum_{i=1}^{n}\alpha(x,x_i)y_i $$
- attention pooling 是
的加权平均 - 将查询
与键 之间的关系建模为 attention weight ,这个权重将被分配给每一个对应值 - 对于任何查询,模型在所有键值对注意力权重都是一个有效的概率分布: 它们是非负的,并且总和为1
如果使用高斯核:
一个键
1.2 Parametric attention pooling¶
将可学习的参数集成到注意力汇聚中,在查询
与非参数的注意力汇聚模型相比, 带参数的模型加入可学习的参数后, 曲线在注意力权重较大的区域变得更不平滑。
2. 注意力分数¶
通过 Attention scoring function 实现对查询和键之间的关系建模(评估query和key的相似度),将其结果输入到 softmax 中得到注意力权重. 最后,注意力汇聚的输出就是基于这些注意力权重的值的加权和。
2.1 Additive Attention¶
当查询和键是 不同长度的矢量 时,可以使用加性注意力作为评分函数。 给定query
等价于将key和query合并起来后放到一个 隐藏大小为 h, 输出大小为1 的单隐藏层MLP.
2.2 Scaled Dot-Product Attention¶
当查询和键是 相同长度 为d时, 可以使用缩放点积注意力.
假设查询和键的所有元素都是独立的随机变量, 并且都满足零均值和单位方差, 那么两个向量的点积的均值为0,方差为d。 为确保无论向量长度如何, 点积的方差在不考虑向量长度的情况下仍然是1.
3. Bahdanau 注意力¶
在原始模型中, 上下文变量
表示编码器在时间步 t 的隐藏层信息, 且既表示key又表示value; 表示解码器在时间步 t'-1 的隐藏层信息, 用于表示 query;- 在每个 decoding time step
, 都会被更新. 假设input sequence 长度为 :
将被输入解码器的循环层, 来生成解码器时间步 t' 的 state
代码¶
class Seq2SeqAttentionDecoder(AttentionDecoder):
def __init__(self, vocab_size, embed_size, num_hiddens, num_layers,
dropout=0, **kwargs):
super(Seq2SeqAttentionDecoder, self).__init__(**kwargs)
#采用additive attention为评分函数
self.attention = d2l.AdditiveAttention(
num_hiddens, num_hiddens, num_hiddens, dropout)
self.embedding = nn.Embedding(vocab_size, embed_size)
self.rnn = nn.GRU(
embed_size + num_hiddens, num_hiddens, num_layers,
dropout=dropout)
self.dense = nn.Linear(num_hiddens, vocab_size)
def init_state(self, enc_outputs, enc_valid_lens, *args):
# outputs的形状为(batch_size,num_steps,num_hiddens).
# hidden_state的形状为(num_layers,batch_size,num_hiddens)
outputs, hidden_state = enc_outputs
return (outputs.permute(1, 0, 2), hidden_state, enc_valid_lens)
def forward(self, X, state):
# enc_outputs的形状为(batch_size,num_steps,num_hiddens).
# hidden_state的形状为(num_layers,batch_size,
# num_hiddens)
enc_outputs, hidden_state, enc_valid_lens = state
# 输出X的形状为(num_steps,batch_size,embed_size)
X = self.embedding(X).permute(1, 0, 2)
outputs, self._attention_weights = [], []
for x in X:
# query的形状为(batch_size,1,num_hiddens)
query = torch.unsqueeze(hidden_state[-1], dim=1)
# context的形状为(batch_size,1,num_hiddens)
# enc_valid_lens是大小为batch_size的向量,每个值表示该样本句子有效长度,算有效长度内的注意力权重,大于长度的部分可以不要管
context = self.attention(
query, enc_outputs, enc_outputs, enc_valid_lens)
# 在特征维度上连结
x = torch.cat((context, torch.unsqueeze(x, dim=1)), dim=-1)
# 将x变形为(1,batch_size,embed_size+num_hiddens)
out, hidden_state = self.rnn(x.permute(1, 0, 2), hidden_state)
outputs.append(out)
self._attention_weights.append(self.attention.attention_weights)
# 全连接层变换后,outputs的形状为
# (num_steps,batch_size,vocab_size)
outputs = self.dense(torch.cat(outputs, dim=0))
return outputs.permute(1, 0, 2), [enc_outputs, hidden_state,
enc_valid_lens]
@property
def attention_weights(self):
return self._attention_weights
4. Self-Attention and Position Encoding¶
4.1 Self-Attention¶
给定输入 tokens序列
将
与CNN,RNN对比¶
- GPU可以作并行运算,所以有并行度来衡量
- 最长路径指,
到 需要经过多少